const cheerio = require("cheerio");
/** 标记符 **/
const brTag = "<br />"; // 在修改html字符串中使用
const brTag2 = "&00br"; // 在修改节点的数据的时候使用
const tableBrTag = "&01br"; // 单元表格中的换行符
const dddTag = "<斜3点Tag />"; // ```
const tabTag = "&0tab"; // \t
const xingTag = "&0xing"; // *
const codeTag = "&0code" // 代码块标记
// 替换的正则
const dddTagR = new RegExp(dddTag, "g");
const brTag2R = new RegExp(brTag2, "g");
const tabTagR = new RegExp(tabTag, "g");
const xingTagR = new RegExp(xingTag, "g");
const tableBrTagR = new RegExp(tableBrTag, "g");

const getContent = ($) => {
  let content = $("body > a > div > span");
  if (content.length == 0) {
    content = $("body > div");
  }
  return content;
};

// 整体思路，通过标记符号来把特殊块给处理了
class Handler {
  constructor() {}
  /**
   * 添加标记
   * @param {string} html html字符串
   * @returns 添加完标记的字符串
   */
  addTag(html) {
    return html;
  }
  /**
   * 在解析后根据标记进行处理
   * @param {any} $ 类似jquery的dom对象
   */
  resolveElement($) {}
  /**
   * 处理信息
   * @param {string} md markdown字符串
   * @returns markdown字符串
   */
  resolveMarkdown(md) {
    return md;
  }
}
/**
 * 解决代码块转换的问题
 */
class CodeHandler extends Handler {
  constructor() {
    super();
  }
  superCodeAddTag(html) {
    let pattern1 = `<div style="--en-codeblock:true;--en-codeblockLanguage:`;
    let strs = html.split(pattern1);
    let result = strs[0];
    if (strs.length > 1) {
      for (let i = 1; i < strs.length; i++) {
        let postItem = strs[i];
        let index2 = postItem.indexOf(";");
        let language = postItem.substring(0, index2);
        if (language.indexOf("-") >= 0) {
          language = language.substring(language.indexOf("-") + 1);
        }
        let connect = `<div class="code" language="${language}" style="--en-codeblock:true;--en-codeblockLanguage:`;
        result += connect;
        result += postItem;
      }
    }
    return result;
  }
  commonCodeAddTag(html) {
    let pattern1 = `<div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace;`;
    let strs = html.split(pattern1);
    let result = strs[0];
    if (strs.length > 1) {
      for (let i = 1; i < strs.length; i++) {
        let postItem = strs[i];
        let index2 = postItem.indexOf(";");
        let language = postItem.substring(0, index2);
        if (language.indexOf("-") >= 0) {
          language = language.substring(language.indexOf("-") + 1);
        }
        let connect = `<div class="code" language="javascript" style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace;`;
        result += connect;
        result += postItem;
      }
    }
    return result;
  }

  // 添加标记
  addTag(html) {
    html = this.superCodeAddTag(html);
    html = this.commonCodeAddTag(html);
    return html;
  }
  // 处理标记
  resolveElement($) {
    // 修改code
    let codes = $(".code");
    for (let i = 0; i < codes.length; i++) {
      let item = codes.eq(i);
      let children = item.children();
      let language = item.attr("language");
      if (children.length === 0 && item.length === 1) {
        // 这是超级笔记的，换行符直接是\n
        // 思路: 把换行符替换成咱们的标记符
        let data = item.text();
        let base64 = Buffer.from(data).toString("base64");
        // 标记符号: codeTag + base64的长度 + ":"
        item.replaceWith(
          `<div>${dddTag + language + brTag2 + codeTag + base64.length+":" + base64 + brTag2 + dddTag}</div>`
        );
      } else {
        // 这是普通笔记的，每一行都是一个div
        // 思路: 只保留一个div，把其他节点拼接成一起，用咱们得标记符来拼接
        let text = dddTag + "javascript" + brTag2;
        for (let j = 0; j < children.length; j++) {
          let child = children.eq(j);
          text += child.html() + brTag2;
        }
        item.replaceWith(`<div>${text + dddTag}</div>`);
      }
    }
  }
  // 处理markdown
  resolveMarkdown(md) {
    md = md.replace(brTag2R, "\n").replace(dddTagR, "```")
    // 解密code部分
    let mdList = md.split(codeTag)
    if(mdList.length>1){
      let result = mdList[0]
      for(let i=1;i<mdList.length;i++){
        let item = mdList[i]
        let start = item.indexOf(":")+1
        let codeLength = Number.parseInt(item.substring(0,start-1))
        let code = item.substring(start,start + codeLength)
        code = Buffer.from(code,"base64").toString()
        result += code + item.substring(start + codeLength)
      }
      md = result
    }
    return md;
  }
}

/**
 * 解决table转换的问题
 */
class TableHandler extends Handler {
  constructor() {
    super();
  }
  /**
   * 根据数组创造Markdown字符串
   * @param {Array} titleArr
   * @param {Array} rowsArr
   */
  getMarkdownTable(titleArr, rowsArr) {
    let colLen = rowsArr[0].length;
    if (titleArr.length === 0) {
      // 无表头
      for (let i = 0; i < colLen; i++) {
        titleArr.push("列" + i);
      }
    }
    // 构建表头
    let result = brTag + "| ";
    for (let i = 0; i < colLen; i++) {
      result += titleArr[i] + " | ";
    }
    result += brTag + "| ";
    for (let i = 0; i < colLen; i++) {
      result += "--- | ";
    }
    result += brTag;
    for (let row = 0; row < rowsArr.length; row++) {
      result += "| ";
      for (let col = 0; col < colLen; col++) {
        result += rowsArr[row][col] + " | ";
      }
      result += brTag;
    }
    return result;
  }
  addTag(html) {
    let pattern1 = "<table";
    let pattern2 = "</table>";
    let tableArr = html.split(pattern2);
    if (tableArr.length > 1) {
      let result = "";
      for (let i = 0; i < tableArr.length - 1; i++) {
        let table = tableArr[i];
        let contentEnd = table.indexOf(pattern1);
        let contentStr = "";
        let tableStr = "";
        if (contentEnd > 0) {
          // 有内容，有table
          contentStr = table.substring(0, contentEnd);
          tableStr = table.substring(contentEnd) + pattern2;
        } else {
          // 只有table
          tableStr = table + pattern2;
        }
        result += contentStr;

        // 处理table
        const $ = cheerio.load(tableStr);
        const titleDom = $("table > thead > tr");
        const tbodyDom = $("table > tbody");
        // 处理title
        let titleArr = [];
        if (titleDom.length > 0) {
          // 存在title
          let tdDom = titleDom.children("td");
          for (let j = 0; j < tdDom.length; j++) {
            // TODO: 多行title这里处理不对
            titleArr.push(titleDom.children(`td:nth(${j})`).text());
          }
        }

        // 处理rows
        let rowsArr = [];
        if (tbodyDom.length > 0) {
          // 存在rows
          let rowsDom = tbodyDom.children("tr");
          for (let i = 0; i < rowsDom.length; i++) {
            let row = rowsDom.eq(i);
            let colLen = row.children().length;
            let rowData = [];
            for (let j = 0; j < colLen; j++) {
              let col = row.children(`td:nth(${j})`).children("div");
              let colData = "";
              for (let k = 0; k < col.length; k++) {
                colData += col.eq(k).html() + tableBrTag;
              }
              rowData.push(colData);
            }
            rowsArr.push(rowData);
          }
        }
        let md = this.getMarkdownTable(titleArr, rowsArr);
        result += "<div>" + md + "</div>";
      }
      result += tableArr[tableArr.length - 1];
      return result;
    }
    return html;
  }
  resolveElement($) {}
  resolveMarkdown(md) {
    return md.replace(tableBrTagR, "<br />");
  }
}
/**
 * 解决列表转换的问题
 */
class ListHandler extends Handler {
  constructor() {
    super();
  }
  addTag(html) {
    return html;
  }

  listByDeep(deep, content) {
    let ret = "";
    for (let i = deep - 1; i > 0; i--) {
      ret += tabTag;
    }
    return (ret += xingTag + " " + content);
  }

  /**
   *
   * @param {string} mode ul还是ol
   * @param {Number} deep 深度
   * @param {Object} curNode 当前节点
   * @param {string} md markdown字符串
   * @returns
   */
  recursion(mode, deep, curNode) {
    let okSign = "li";
    let endeepSign = mode;
    let children = curNode.children();
    let md = "";
    for (let i = 0; i < children.length; i++) {
      let child = children.eq(i);
      let tagName = child[0].tagName;
      if (tagName === okSign) {
        let divNode = child.children("div");
        if (divNode.length > 0) {
          md += this.listByDeep(deep, divNode.html()) + brTag;
        } else {
          let pNode = child.children("p");
          if (pNode.length > 0) {
            md += this.listByDeep(deep, pNode.html()) + brTag;
          } else {
            // 直接输出
            md += this.listByDeep(deep, child.html()) + brTag;
          }
        }
      } else if (tagName === endeepSign) {
        md += this.recursion(mode, deep + 1, child, md);
      } else if (tagName === "a") {
        let liNode = child.children("li");
        if (liNode.length > 0) {
          md += this.listByDeep(deep, liNode.children("div").html()) + brTag;
        }
      }
    }
    return md;
  }

  resolveElement($) {
    const content = getContent($);
    let ulDoms = content.children("ul");
    let olDoms = content.children("ol");
    // 有序列表
    for (let i = 0; i < ulDoms.length; i++) {
      let md = "";
      let ulDom = ulDoms.eq(i);
      // 先看看父级是不是ul
      if (ulDom.parent()[0].tagName === "ul") {
        continue;
      }

      md = this.recursion("ul", 1, ulDom, md);
      ulDom.replaceWith(`<div>${md}</div>`);
    }
    // 无序列表
    for (let i = 0; i < olDoms.length; i++) {
      let md = "";
      let olDom = olDoms.eq(i);
      if (olDom.parent()[0].tagName === "ol") {
        continue;
      }
      md = this.recursion("ol", 1, olDom, md);
      olDom.replaceWith(`<div>${md}</div>`);
    }
  }
  resolveMarkdown(md) {
    return md.replace(tabTagR, "\t").replace(xingTagR,"*")
  }
}
/**
 * 解决普通笔记的文本格式转换的问题
 */
class TextHandler extends Handler {
  constructor() {
    super();
  }
  addTag(html) {
    return html;
  }

  resolveElement($) {
    const content = getContent($);
    // 遍历所有节点
    let spanNodes = content.find("span");
    for (let i = 0; i < spanNodes.length; i++) {
      let curNode = spanNodes.eq(i);
      // 检查是否加粗和斜体
      let text = curNode.text();
      let fontWeight = curNode.css("font-weight");
      let fontStyle = curNode.css("font-style");
      let flag = false;
      if (fontWeight === "bold") {
        text = "<b>" + text + "</b>";
        flag = true;
      }
      if (fontStyle === "italic") {
        text = "<i>" + text + "</i>";
        flag = true;
      }
      if (flag) curNode.replaceWith(text);
    }
    // 将a标签的href全部转码
    let imgNodes = content.find("img")
    for(let i=0;i<imgNodes.length;i++){
      let imgNode = imgNodes.eq(i)
      let src = imgNode.attr("src")
      if(src && src.trim() !==""){
        imgNode.attr("src",encodeURI(src))
      }
    }
  }
  resolveMarkdown(md) {
    return md;
  }
}

module.exports = {
  Handler,
  CodeHandler,
  TableHandler,
  ListHandler,
  TextHandler,
};
